package br.ufrn.cerescaico.sepe;

import br.ufrn.cerescaico.sepe.beans.Area;
import br.ufrn.cerescaico.sepe.beans.Atividade;
import br.ufrn.cerescaico.sepe.beans.Categoria;
import br.ufrn.cerescaico.sepe.beans.Email;
import br.ufrn.cerescaico.sepe.beans.Participante;
import br.ufrn.cerescaico.sepe.beans.Perfil;
import br.ufrn.cerescaico.sepe.beans.Trabalho;
import br.ufrn.cerescaico.sepe.beans.Usuario;
import br.ufrn.cerescaico.sepe.bo.AreaBO;
import br.ufrn.cerescaico.sepe.bo.AtividadeBO;
import br.ufrn.cerescaico.sepe.bo.CategoriaBO;
import br.ufrn.cerescaico.sepe.bo.EmailBO;
import br.ufrn.cerescaico.sepe.bo.ParticipanteBO;
import br.ufrn.cerescaico.sepe.bo.PerfilBO;
import br.ufrn.cerescaico.sepe.bo.SepeException;
import br.ufrn.cerescaico.sepe.bo.TrabalhoBO;
import br.ufrn.cerescaico.sepe.bo.UsuarioBO;
import br.ufrn.cerescaico.sepe.dao.util.PagerBean;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;

import org.hibernate.Session;

/**
 * Fachada do sistema SEPE.
 * @author Taciano de Morais Silva - tacianosilva@gmail.com
 * @version 01/06/2010, 14h28m, Taciano de Morais Silva - tacianosilva@gmail.com
 * @since 01/06/2010, 14h28m
 */
public class Sepe {

    /**
     * Logger.
     */
    private Logger logger = Logger.getLogger(Sepe.class);

    /**
     * Instância única da fachada.
     */
    private volatile static Sepe instance = null;

    /**
     * Construtor privado da fachada.
     */
    private Sepe() {
    }

    /**
     * Sessão abertas com o banco para lazy instantiation.
     */
    private List<Session> sessoes;

    /**
     * Usuário da sessão.
     */
    private Usuario usuarioSessao;

    /**
     * Retorna a instância única do singleton Sepe (fachada).
     * @return A instância do Sepe.
     */
    public static Sepe getInstance() {
        if (instance == null) {
            synchronized(Sepe.class) {
            	Sepe inst = instance;
                if (inst == null) {
                    instance = new Sepe();
                }
            }
        }
        return instance;
    }

    /**
     * @return
     * @throws SepeException
     */
    public List<Categoria> listarCategoriasTrabalhos() throws SepeException {
        CategoriaBO bo = createCategoriaBO();
        return bo.listarCategoriasTrabalhos();
    }

    /**
     * @return
     * @throws SepeException
     */
    public List<Categoria> listarCategoriasAtividades() throws SepeException {
        CategoriaBO bo = createCategoriaBO();
        return bo.listarCategoriasAtividades();
    }

    /**
     * @return
     * @throws SepeException
     */
    public List<Area> listarAreas() throws SepeException {
        AreaBO bo = createAreaBO();
        return bo.listar();
    }
    
        /**
     * Autentica o usuários. Se não for possível autenticar o usuário será
     * retornado <code>null</code>.
     * @param username Login do usuário.
     * @param password Senha do usuário.
     * @return O usuário autenticado, ou <code>null</code> caso contrário.
     * @throws SepeException
     */
    public Usuario autenticarUsuario(String username, String password) throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        return bo.autenticar(username, password);
    }

    /**
     * Lista todos os usuários cadastrados no sistema.
     * @return A lista de usuários.
     */
    public List<Usuario> listarUsuarios() throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        return bo.listar();
    }

    /**
     * Busca o usuário a partir do seu identificador.
     * @param id O identificador do usuário.
     * @return O usuário localizado.
     */
    public Usuario buscarUsuario(Integer id) throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        return bo.buscar(id);
    }

    /**
     * Incluir usuário no sistema.
     * @param usuario O usuário a ser incluído.
     */
    public void incluirUsuario(Usuario usuario) throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        bo.incluir(usuario);
    }

    /**
     * Alterar os dados do usuário.
     * @param usuario O usuário a ser alterado.
     */
    public void alterarUsuario(Usuario usuario) throws SepeException{
        UsuarioBO bo = createUsuarioBO();
        bo.alterar(usuario);
    }

    /**
     * Exclui o usuário do sistema.
     * @param usuario O usuário a ser excluído.
     */
    public void excluirUsuario(Usuario usuario) throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        bo.excluir(usuario);
    }

    /**
     * Exclui o usuário do sistema.
     * @param id O identificador do usuário a ser excluído.
     */
    public void excluirUsuario(Integer id) throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        bo.excluir(id);
    }

    /**
     * Pesquisar usuário filtrando por login, nome e perfil.
     * @param login O login do usuário.
     * @param nome O nome do usuário.
     * @param perfil O perfil do usuário.
     */
    public List<Usuario> pesquisarUsuario(String login, String nome, Perfil perfil)
            throws SepeException {
        UsuarioBO bo = createUsuarioBO();
        return bo.pesquisar(login, nome, perfil);
    }

    /**
     * Busca o perfil a partir do seu identificador.
     * @param id O identificador do perfil.
     * @return O perfil localizado.
     */
    public Perfil buscarPerfil(Integer id) throws SepeException {
        PerfilBO bo = createPerfilBO();
        return bo.buscar(id);
    }

    public Participante autenticarParticipante(String username, String password) throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        return bo.autenticar(username, password);
    }

    public void incluirParticipante(Participante participante) throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        bo.incluir(participante);
    }

    public void alterarParticipante(Participante participante) throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        bo.alterar(participante);
    }

    public Participante buscarParticipante(Integer id) throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        return bo.buscar(id);
    }

    /**
     * Lista todos os participantes cadastrados no sistema.
     * @return A lista de participantes.
     */
    public List<Participante> listarParticipantes() throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        return bo.listar();
    }

    /**
     * Lista todos os participantes cadastrados no sistema.
     * @return A lista de participantes.
     */
    public List<Participante> listarPartOrdenadoPorEmail() throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        return bo.listarOrdenadoPorEmail();
    }

    public void reenviarSenhaParticipante(Participante participante) throws SepeException {
        ParticipanteBO bo = createParticipanteBO();
        bo.reenviarSenha(participante);
    }

    public List<Trabalho> listarTrabalhos(PagerBean pager) throws SepeException {
        TrabalhoBO bo = createTrabalhoBO();
        return bo.listar(pager);
    }

    public void submeterTrabalho(Trabalho trabalho) throws SepeException {
        TrabalhoBO bo = createTrabalhoBO();
        bo.submeter(trabalho);
    }

    public List<Atividade> listarAtividades(PagerBean pager) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        return bo.listar(pager);
    }

    public List<Atividade> listarAtividades(String horario) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        return bo.listar(horario);
    }

    public List<Atividade> listarAtividades(Participante participante) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        return bo.listar(participante);
    }

    public Atividade buscarAtividade(Integer id) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        return bo.buscar(id);
    }

     public void alterarAtividade(Atividade atividade) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        bo.alterar(atividade);
    }

    public void addParticipanteAtividade(Atividade atividade, Participante participante) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        bo.addParticipante(atividade, participante);
    }

    public void submeterAtividade(Atividade atividade) throws SepeException {
        AtividadeBO bo = createAtividadeBO();
        bo.submeter(atividade);
    }

    public void enviarEmail(Email email) throws SepeException {
        EmailBO bo = createEmailBO();
        bo.enviar(email);
    }

    /**
     * @return Um objeto de negócio.
     */
    private EmailBO createEmailBO() {
        return new EmailBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private CategoriaBO createCategoriaBO() {
        return new CategoriaBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private AreaBO createAreaBO() {
        return new AreaBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private UsuarioBO createUsuarioBO() {
        return new UsuarioBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private PerfilBO createPerfilBO() {
        return new PerfilBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private ParticipanteBO createParticipanteBO() {
        return new ParticipanteBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private TrabalhoBO createTrabalhoBO() {
        return new TrabalhoBO(Sepe.getInstance());
    }

    /**
     * @return Um objeto de negócio.
     */
    private AtividadeBO createAtividadeBO() {
        return new AtividadeBO(Sepe.getInstance());
    }

    /**
     * Retorna o usuário logado na sessão.
     * @return O usuário da sessão.
     */
    public Usuario getUsuarioSessao() {
        return usuarioSessao;
    }

    /**
     * Modifica o usuário da sessão.
     * @param usuarioSessao O novo usuário da sessão.
     */
    public void setUsuarioSessao(Usuario usuarioSessao) {
        this.usuarioSessao = usuarioSessao;
    }

    /**
     * Adiciona uma sessão do hibernate na fachada, no caso de uma consulta lazy.
     * @param session A sessão do hibernate aberta.
     */
    public void addSessaoHibernate(Session session) {
        if (sessoes == null) {
            sessoes = new ArrayList<Session>();
        }
        if (!sessoes.contains(session)) {
            sessoes.add(session);
        }
    }

    /**
     * Fecha todas as sessões do hibernate abertas.
     */
    public void closeSessaoHibernate() {
        if (sessoes != null) {
            Iterator<Session> it = sessoes.iterator();
            while (it.hasNext()) {
                Session session = (Session) it.next();
                try {
                    session.close();
                } catch (HibernateException ignored) {
                    logger.warn("Couldn't close Session");
                }
            }
            sessoes = new ArrayList<Session>();
        }
    }
}
